﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace System.Windows.Controls
{
    /// <summary>
    /// Implements a view container control which shows one <see cref="MultiViewPanel"/> instance at a time, and can animated between multiple such view instances.
    /// </summary>
    public class MultiView : Panel
    {
        /// <summary>
        /// Initializes a <see cref="MultiView"/> instance.
        /// </summary>
        public MultiView()
        {

        }

        /// <summary>
        /// Identifies the <see cref="SelectedPanel"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty SelectedPanelProperty = RegisterDependencyProperty(typeof(MultiView), "SelectedPanel", typeof(MultiViewPanel));

        /// <summary>
        /// Identifies the <see cref="SelectedIndex"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty SelectedIndexProperty = RegisterDependencyProperty(typeof(MultiView), "SelectedIndex", typeof(int));

        #region Dependency Property Helpers

        /// <summary>
        /// A static helper method for registering a <see cref="System.Windows.DependencyProperty"/>.
        /// </summary>
        /// <param name="controlType">The <see cref="System.Type"/> for the control to which the property belongs</param>
        /// <param name="name">The name of the property</param>
        /// <param name="propertyType">The <see cref="System.Type"/> for the value of the property</param>
        /// <returns></returns>
        protected static DependencyProperty RegisterDependencyProperty(Type controlType, string name, Type propertyType)
        {
            DependencyProperty prop = DependencyProperty.Register(name, propertyType, controlType, new PropertyMetadata(new PropertyChangedCallback(OnHandleDependencyPropertyChanged)));
            return prop;
        }

        /// <summary>
        /// A static method for handling <see cref="System.Windows.DependencyProperty"/> changes.
        /// </summary>
        /// <param name="obj">The <see cref="System.Windows.DependencyObject"/> against which the property change should be applied</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> event argument</param>
        protected static void OnHandleDependencyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (obj is MultiView)
            {
                ((MultiView)obj).OnDependencyPropertyChanged(e);
            }
        }

        /// <summary>
        /// A virtual handler for processing <see cref="System.Windows.DependencyProperty"/> changes in derived controls.
        /// </summary>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> event argument</param>
        protected virtual void OnDependencyPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == SelectedPanelProperty)
            {
                this.SelectedPanel = (MultiViewPanel)e.NewValue;
            }
            else if (e.Property == SelectedIndexProperty)
            {
                this.SelectedIndex = (int)e.NewValue;
            }
        }

        #endregion

        /// <summary>
        /// Gets/Sets the index of the currently selected (Visible) <see cref="MultiViewPanel"/>.
        /// </summary>
        public int SelectedIndex
        {
            get { return (int)GetValue(SelectedIndexProperty); }
            set
            {
                if (this.Children.Count > 0)
                {
                    SetValue(SelectedIndexProperty, Math.Min(Math.Max(0, value), this.Children.Count - 1));

                    for (int index = 0; index < this.Children.Count; index++)
                    {
                        if (index == this.SelectedIndex)
                            this.SelectedPanel = this.Children[this.SelectedIndex] as MultiViewPanel;
                        else
                        {
                            MultiViewPanel panel = this.Children[index] as MultiViewPanel;
                            if (panel != null) panel.Obscure();
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets/Sets the currently selected (Visible) <see cref="MultiViewPanel"/>.
        /// </summary>
        public MultiViewPanel SelectedPanel
        {
            get { return (MultiViewPanel)GetValue(SelectedPanelProperty); }
            set
            {
                if (this.SelectedPanel != null && this.SelectedPanel != value)
                {
                    this.SelectedPanel.Obscure();
                }

                if (this.Children.Contains(value))
                {
                    int index = this.Children.IndexOf(value);

                    SetValue(SelectedPanelProperty, value);
                    SetValue(SelectedIndexProperty, index);

                    if (this.SelectedPanel != null)
                    {
                        this.SelectedPanel.Reveal();
                    }

                    this.InvalidateArrange();
                }
            }
        }

        /// <summary>
        /// (Override) Provides new arrangement behavior for arranging the <see cref="MultiViewPanel"/> children.
        /// </summary>
        /// <param name="finalSize"></param>
        /// <returns></returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            Rect rect = new Rect(0, 0, finalSize.Width, finalSize.Height);

            foreach (UIElement element in this.Children)
            {
                if (element is MultiViewPanel)
                {
                    MultiViewPanel panel = (MultiViewPanel)element;
                    panel.Arrange(rect);
                }
                else if (element is ControlProgress)
                {
                    ControlProgress progress = (ControlProgress)element;
                    progress.Arrange(new Rect(rect.X + ((rect.Width - 64) / 2), rect.Y + ((rect.Height - 64) / 2), 64, 64));
                }
                else
                {
                    throw new InvalidOperationException(SystemResources.ERR_MULTIVIEW_CHILD_TYPE);
                }
            }

            return finalSize;
        }

        #region Progress

        /// <summary>
        /// An instance of a <see cref="ControlProgress"/> control.
        /// </summary>
        protected ControlProgress   progress = null;

        /// <summary>
        /// Gets a boolean value indicating whether the progress element is visible or not for this <see cref="MultiView"/> instance.
        /// </summary>
        public bool ProgressActive
        {
            get { return (progress == null ? false : progress.Active); }
        }

        /// <summary>
        /// Shows the animated progress display for this control.
        /// </summary>
        /// <param name="show">A boolean value indictating whether to show (true) or hide (false) the progress control</param>
        public void ShowProgress(bool show)
        {
            if (progress == null)
            {
                progress = new ControlProgress();
                this.Children.Add(progress);

                progress.ApplyTemplate();
            }

            if (show)
            {
                progress.Show();
            }
            else
            {
                progress.Hide();
            }
        }

        #endregion
    }

    /// <summary>
    /// Implements a custom <see cref="AnimatedPanel"/> derived class for use as an item panel in the <see cref="MultiView"/> control.
    /// </summary>
    public class MultiViewPanel : AnimatedPanel
    {
        /// <summary>
        /// Initializes a <see cref="MultiViewPanel"/> instance.
        /// </summary>
        public MultiViewPanel()
        {
        }

        /// <summary>
        /// (Override) Provides simple layout functionality for stretching all children to the full size of the <see cref="MultiViewPanel"/> instance.
        /// </summary>
        /// <param name="finalSize"></param>
        /// <returns></returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (UIElement element in this.Children)
            {
                if (element is FrameworkElement)
                {
                    FrameworkElement frameElement = (FrameworkElement)element;
                    frameElement.Width = this.Width;
                    frameElement.Height = this.Height;
                }

                element.Measure(finalSize);
                element.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
            }

            return base.ArrangeOverride(finalSize);
        }
    }
}
